using System.Collections.Generic;
using System.Linq.Expressions;

namespace K3Cloud.WebApi.Core.IoC.Extensions
{
    /// <summary>
    /// 冗余条件移除规则
    /// 移除重复的条件表达式
    /// 例如: (x.A && x.A && x.B) => (x.A && x.B)
    /// </summary>
    public class RedundantRemovalRule : IOptimizationRule
    {
        public string Name => "冗余移除";

        public int Priority => 3;

        public bool CanApply(Expression expression)
        {
            // 检查是否包含冗余条件
            var checker = new RedundantChecker();
            checker.Visit(expression);
            return checker.HasRedundancy;
        }

        public Expression Apply(Expression expression)
        {
            return new RedundantRemovalVisitor().Visit(expression) ?? expression;
        }

        /// <summary>
        /// 冗余检查器
        /// </summary>
        private class RedundantChecker : ExpressionVisitor
        {
            public bool HasRedundancy { get; private set; }
            private readonly HashSet<string> _expressions = new HashSet<string>();

            protected override Expression VisitBinary(BinaryExpression node)
            {
                if (node.NodeType == ExpressionType.AndAlso || node.NodeType == ExpressionType.OrElse)
                {
                    var leftStr = node.Left.ToString();
                    var rightStr = node.Right.ToString();

                    if (_expressions.Contains(leftStr) || _expressions.Contains(rightStr) || leftStr == rightStr)
                    {
                        HasRedundancy = true;
                    }

                    _expressions.Add(leftStr);
                    _expressions.Add(rightStr);
                }

                return base.VisitBinary(node);
            }
        }

        /// <summary>
        /// 冗余移除访问器
        /// </summary>
        private class RedundantRemovalVisitor : ExpressionVisitor
        {
            private readonly HashSet<string> _seenConditions = new HashSet<string>();

            protected override Expression VisitBinary(BinaryExpression node)
            {
                if (node.NodeType == ExpressionType.AndAlso)
                {
                    var left = Visit(node.Left);
                    var right = Visit(node.Right);

                    if (left == null || right == null)
                        return node;

                    var leftStr = left.ToString();
                    var rightStr = right.ToString();

                    // 如果左右完全相同: x && x => x
                    if (leftStr == rightStr)
                    {
                        return left;
                    }

                    // 如果右侧条件已经在左侧出现过
                    if (_seenConditions.Contains(rightStr))
                    {
                        return left;
                    }

                    // 如果左侧条件已经在之前出现过
                    if (_seenConditions.Contains(leftStr))
                    {
                        _seenConditions.Add(rightStr);
                        return right;
                    }

                    _seenConditions.Add(leftStr);
                    _seenConditions.Add(rightStr);

                    if (left != node.Left || right != node.Right)
                    {
                        return Expression.AndAlso(left, right);
                    }

                    return node;
                }

                if (node.NodeType == ExpressionType.OrElse)
                {
                    var left = Visit(node.Left);
                    var right = Visit(node.Right);

                    if (left == null || right == null)
                        return node;

                    var leftStr = left.ToString();
                    var rightStr = right.ToString();

                    // 如果左右完全相同: x || x => x
                    if (leftStr == rightStr)
                    {
                        return left;
                    }

                    if (left != node.Left || right != node.Right)
                    {
                        return Expression.OrElse(left, right);
                    }

                    return node;
                }

                return base.VisitBinary(node);
            }

            public override Expression? Visit(Expression? node)
            {
                // 每次访问新的lambda或独立表达式时重置seen集合
                if (node is LambdaExpression)
                {
                    _seenConditions.Clear();
                }

                return base.Visit(node);
            }
        }
    }
}
